# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: jaewon

import logging

from coin.exchange.base.order_gateway import OrderType, OrderSide
from coin.exchange.huobi_futures.kr_rest.futures_product import HuobiFuturesProduct
from coin.exchange.huobi_futures.order_gateway import HuobiFuturesOrderGateway
from coin.exchange.huobi_futures.kr_rest.native_private_client import HuobiFuturesOrderSide
from coin.strategy.executor.passive.simple_mm_executor import \
  SimpleMMExecutor
from coin.strategy.executor.util.traits import (order_logger, FuturesExecutorTraits)
from coin.strategy.executor.aggressive.aggressive_executor import \
  AggressiveExecutor


class HuobiFuturesSimpleTraits(FuturesExecutorTraits):
  BuyOrderSide = [HuobiFuturesOrderSide.BUY_OPEN, HuobiFuturesOrderSide.BUY_CLOSE]
  SellOrderSide = [HuobiFuturesOrderSide.SELL_OPEN, HuobiFuturesOrderSide.SELL_CLOSE]

  def __init__(self, executor_config, order_gateway, logger=None):
    logger = logger or logging.getLogger('HuobiFuturesSimpleMMExecutor')
    super().__init__(executor_config, order_gateway, logger)
    self._hard_close = False

  def set_hard_close(self):
    self._hard_close = True
    self._submit_order_impl = self._submit_order_hard_close_impl
    self._logger.info("****HARD CLOSE OVERRIDDEN****")
    self._logger.info("****HARD CLOSE OVERRIDDEN****")
    self._logger.info("****HARD CLOSE OVERRIDDEN****")

  def _submit_order_hard_close_impl(self, product, price, qty, order_side, post_only=None, close_only=True):
    if not self.og.submittable():
      self._logger.warn('Skip submission: %s %f@%f' % (product, qty, price))
      return False

    position = self.og.og_info.get_product_position_proto(product)
    short_position = abs(position.available_short_position)
    long_position = abs(position.available_long_position)
    if self.config.HasField('leverage'):
      leverage = self.config.leverage
    else:
      leverage = 10
    order_type = OrderType.LIMIT

    if order_side == OrderSide.SELL:
      qty = min(qty, long_position)
      if qty == 0:
        return False
      self._og.submit(product, price, qty, HuobiFuturesOrderSide.SELL_CLOSE,
                      order_type, leverage=leverage, post_only=post_only)
      self._logger.info('SELL_CLOSE  %s %f@%.3f', product, qty, price)
    elif order_side == OrderSide.BUY:
      qty = min(qty, short_position)
      if qty == 0:
        return False
      self._og.submit(product, price, qty, HuobiFuturesOrderSide.BUY_CLOSE,
                      order_type, leverage=leverage, post_only=post_only)
      self._logger.info('BUY_CLOSE  %s %f@%.3f', product, qty, price)

  def _get_position(self, product):
    return self.og.get_position(product)

  def _submit_order_impl(self, product, price, qty, order_side, post_only=False, close_only=False):
    if not self.og.submittable():
      self._logger.warn('Skip submission: %s %f@%f' % (product, qty, price))
      return False

    sell_working_close_order_qty = 0
    buy_working_close_order_qty = 0
    for order in self._og.get_working_order() + list(self._og.recent_cancel_accepted.values()):
      if order.product != self.product:
        continue
      if order.order_side == HuobiFuturesOrderSide.SELL_CLOSE:
        sell_working_close_order_qty += order.qty
      elif order.order_side == HuobiFuturesOrderSide.BUY_CLOSE:
        buy_working_close_order_qty += order.qty
    position = self.og.og_info.get_product_position_proto(product)
    short_position = abs(position.available_short_position)
    long_position = abs(position.available_long_position)
    if self.config.HasField('leverage'):
      leverage = self.config.leverage
    else:
      leverage = 10
    order_type = OrderType.LIMIT
    if order_side == OrderSide.SELL:
      if close_only:
        max_position_to_close = long_position - (self.config.maintain_open_pos
                                                 + sell_working_close_order_qty)
        if max_position_to_close > 0 and \
           not self.og.has_recent_close_error(HuobiFuturesOrderSide.SELL_CLOSE):
          qty = min(qty, max_position_to_close)
          self._og.submit(product,
                          price,
                          qty,
                          HuobiFuturesOrderSide.SELL_CLOSE,
                          order_type,
                          leverage=leverage,
                          post_only=post_only)
          self._logger.info('SELL_CLOSE  %s %f@%.3f', product, qty, price)
        else:
          self._logger.warn('Skip submission due to close only check: %s %f@%f' %
                            (product, qty, price))
      elif long_position >= max(
          self.config.maintain_open_pos,
          qty) + sell_working_close_order_qty and not self.og.has_recent_close_error(
              HuobiFuturesOrderSide.SELL_CLOSE):
        qty = min(qty, long_position)
        self._og.submit(product,
                        price,
                        qty,
                        HuobiFuturesOrderSide.SELL_CLOSE,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('SELL_CLOSE  %s %f@%.3f', product, qty, price)
      else:
        self._og.submit(product,
                        price,
                        qty,
                        HuobiFuturesOrderSide.SELL_OPEN,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('SELL_OPEN  %s %f@%.3f', product, qty, price)
    elif order_side == OrderSide.BUY:
      if close_only:
        max_position_to_close = short_position - (self.config.maintain_open_pos
                                                  + buy_working_close_order_qty)
        if max_position_to_close > 0 and \
           not self.og.has_recent_close_error(HuobiFuturesOrderSide.BUY_CLOSE):
          qty = min(qty, max_position_to_close)
          self._og.submit(product,
                          price,
                          qty,
                          HuobiFuturesOrderSide.BUY_CLOSE,
                          order_type,
                          leverage=leverage,
                          post_only=post_only)
          self._logger.info('BUY_CLOSE  %s %f@%.3f', product, qty, price)
        else:
          self._logger.warn('Skip submission due to close only check: %s %f@%f' %
                            (product, qty, price))
      elif short_position >= max(
          self.config.maintain_open_pos,
          qty) + buy_working_close_order_qty and not self.og.has_recent_close_error(
              HuobiFuturesOrderSide.BUY_CLOSE):
        qty = min(qty, short_position)
        self._og.submit(product,
                        price,
                        qty,
                        HuobiFuturesOrderSide.BUY_CLOSE,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('BUY_CLOSE  %s %f@%.3f', product, qty, price)
      else:
        self._og.submit(product,
                        price,
                        qty,
                        HuobiFuturesOrderSide.BUY_OPEN,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('BUY_OPEN  %s %f@%.3f', product, qty, price)
    else:
      raise ValueError('Unknown order side: %s' % order_side)

  @order_logger
  def _submit_order(self, product, price, qty, order_side):
    self._submit_order_impl(product, price, qty, order_side, close_only=self.config2.close_only)

  @order_logger
  def _submit_pass_order(self, product, price, qty, order_side):
    self._submit_order_impl(product,
                            price,
                            qty,
                            order_side,
                            post_only=self.config.post_only,
                            close_only=self.config2.close_only)

  def _cancel_orders_impl(self, order_id_list):
    self.og.cancel_multiple(order_id_list)


class HuobiFuturesSimpleMMExecutor(SimpleMMExecutor):
  ProductType = HuobiFuturesProduct
  OGType = HuobiFuturesOrderGateway
  Traits = HuobiFuturesSimpleTraits


class HuobiFuturesSimpleAggExecutor(AggressiveExecutor):
  ProductType = HuobiFuturesProduct
  OGType = HuobiFuturesOrderGateway
  Traits = HuobiFuturesSimpleTraits
